package middleware

import (
	"go-project/common"
	"go-project/common/base"
	"go-project/common/base/resp"
	"go-project/internal/dao"
	"go-project/internal/model"
	"time"

	jwt "github.com/appleboy/gin-jwt/v2"
	"github.com/gin-gonic/gin"
)

var (
	identityKey = jwt.IdentityKey
)

func GinJWTMiddleware() (*jwt.GinJWTMiddleware, error) {
	middleware, err := jwt.New(&jwt.GinJWTMiddleware{
		//Realm:           "test zone",
		Key:             []byte(common.CONFIG.JWT.Signed),
		Timeout:         time.Duration(common.CONFIG.JWT.TTL) * time.Second,
		MaxRefresh:      time.Hour,
		IdentityKey:     identityKey,
		PayloadFunc:     payloadFunc(),
		IdentityHandler: identityHandler(),
		Authenticator:   authenticator(),
		Authorizator:    authorizator(),
		Unauthorized:    unauthorized(),
		TokenLookup:     "header: Authorization, query: token, cookie: jwt",
		TokenHeadName:   "Bearer",
		TimeFunc:        time.Now,
	})
	if err != nil {
		return nil, err
	}
	return middleware, nil
}

func payloadFunc() func(data any) jwt.MapClaims {
	return func(data any) jwt.MapClaims {
		if v, ok := data.(*model.User); ok {
			return jwt.MapClaims{
				identityKey: v.ID,
				"username":  v.Username,
			}
		}
		return jwt.MapClaims{}
	}
}

func identityHandler() func(c *gin.Context) any {
	return func(c *gin.Context) any {
		claims := jwt.ExtractClaims(c)
		return &model.User{
			Model: base.Model{
				ID: uint(claims[identityKey].(float64)),
			},
			Username: claims["username"].(string),
		}
	}
}

func authenticator() func(c *gin.Context) (any, error) {
	return func(c *gin.Context) (any, error) {
		loginUser := new(model.UserDto)
		if err := c.BindJSON(loginUser); err != nil {
			resp.BadRequest(c, err.Error())
		}
		userEnt, err := dao.UserDao.GetOne(common.DB, &model.User{Username: loginUser.Username})
		if err != nil {
			return nil, err
		}
		//loginUser.Password = utils.MD5(loginUser.Password)
		if userEnt.Password == loginUser.Password {
			return userEnt, nil
		}
		return nil, jwt.ErrFailedAuthentication
	}
}

func authorizator() func(data any, c *gin.Context) bool {
	return func(data any, c *gin.Context) bool {
		if v, ok := data.(*model.User); ok && v.Username == "admin" {
			return true
		}
		return false
	}
}

func unauthorized() func(c *gin.Context, code int, message string) {
	return func(c *gin.Context, code int, message string) {
		c.JSON(code, resp.Response{
			Code: code,
			Msg:  message,
		})
	}
}

func GetUser(c *gin.Context) *model.User {
	user, _ := c.Get(identityKey)
	return user.(*model.User)
}
